﻿#include <iostream>
#include <fstream>
#include <iomanip>
#include <ctime>
#include <pcap.h>
#include <map>
#include <string>
#include <thread>
#include <chrono>
#include <sstream>
#include <winsock2.h>  // Windows网络编程头文件
#include <Ws2tcpip.h>  // 包含inet_ntop等函数
#pragma comment(lib, "ws2_32.lib")  // 链接ws2_32库
#pragma comment(lib, "wpcap.lib")    // 链接NPcap库
#pragma comment(lib, "Packet.lib")   // 链接NPcap库

// 以太网帧头部结构体
struct eth_header {
    u_char  ether_dhost[6];    // 目标MAC地址
    u_char  ether_shost[6];    // 源MAC地址
    u_short ether_type;        // 协议类型
};

// IP头部结构体
struct ip_header {
    u_char  ip_vhl;            // 版本和首部长度
    u_char  ip_tos;            // 服务类型
    u_short ip_len;            // 总长度
    u_short ip_id;             // 标识
    u_short ip_off;            // 标志和片偏移
    u_char  ip_ttl;            // 生存时间
    u_char  ip_p;              // 协议
    u_short ip_sum;            // 首部校验和
    struct  in_addr ip_src;    // 源IP地址
    struct  in_addr ip_dst;    // 目标IP地址
};

// 将MAC地址转换为字符串
std::string mac_to_string(const u_char* mac) {
    char buffer[18];
    snprintf(buffer, sizeof(buffer), "%02X-%02X-%02X-%02X-%02X-%02X",
        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    return std::string(buffer);
}

// 将IP地址转换为字符串
std::string ip_to_string(const struct in_addr& ip) {
    char buffer[INET_ADDRSTRLEN];
    if (InetNtopA(AF_INET, &ip, buffer, INET_ADDRSTRLEN) == nullptr) {
        // 处理转换失败的情况
        std::cerr << "InetNtopA 转换失败: " << WSAGetLastError() << std::endl;
        return "";
    }
    return std::string(buffer);
}

// 获取当前时间的字符串表示
std::string get_current_time() {
    auto now = std::chrono::system_clock::now();
    auto in_time_t = std::chrono::system_clock::to_time_t(now);
    std::tm localTime;
    if (localtime_s(&localTime, &in_time_t) != 0) {
        std::cerr << "localtime_s 转换失败" << std::endl;
        return "";
    }
    std::stringstream ss;
    ss << std::put_time(&localTime, "%Y-%m-%d %H:%M:%S");
    return ss.str();
}

// 数据包处理回调函数
void packet_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data) {
    std::ofstream log_file("network_log.csv", std::ios::app);
    if (!log_file.is_open()) {
        std::cerr << "无法打开日志文件！" << std::endl;
        return;
    }

    eth_header* eth = (eth_header*)pkt_data;
    if (ntohs(eth->ether_type) == 0x0800) { // IPv4
        ip_header* ip = (ip_header*)(pkt_data + sizeof(eth_header));
        std::string src_mac = mac_to_string(eth->ether_shost);
        std::string dst_mac = mac_to_string(eth->ether_dhost);
        std::string src_ip = ip_to_string(ip->ip_src);
        std::string dst_ip = ip_to_string(ip->ip_dst);
        int frame_length = header->len;

        log_file << get_current_time() << ","
            << src_mac << ","
            << src_ip << ","
            << dst_mac << ","
            << dst_ip << ","
            << frame_length << std::endl;
    }
    log_file.close();
}

// 统计函数
void statistics() {
    while (true) {
        std::this_thread::sleep_for(std::chrono::minutes(1));

        std::ifstream log_file("network_log.csv");
        if (!log_file.is_open()) {
            std::cerr << "无法打开日志文件！" << std::endl;
            continue;
        }

        std::map<std::string, int> src_mac_stats;
        std::map<std::string, int> dst_mac_stats;
        std::map<std::string, int> src_ip_stats;
        std::map<std::string, int> dst_ip_stats;

        std::string line;
        while (std::getline(log_file, line)) {
            size_t pos = 0;
            std::string token;
            std::string src_mac, src_ip, dst_mac, dst_ip;
            int frame_length;

            // 解析时间
            pos = line.find(',');
            line = line.substr(pos + 1);

            // 解析源MAC
            pos = line.find(',');
            src_mac = line.substr(0, pos);
            line = line.substr(pos + 1);

            // 解析源IP
            pos = line.find(',');
            src_ip = line.substr(0, pos);
            line = line.substr(pos + 1);

            // 解析目标MAC
            pos = line.find(',');
            dst_mac = line.substr(0, pos);
            line = line.substr(pos + 1);

            // 解析目标IP
            pos = line.find(',');
            dst_ip = line.substr(0, pos);
            line = line.substr(pos + 1);

            // 解析帧长度
            frame_length = std::stoi(line);

            // 更新统计信息
            src_mac_stats[src_mac] += frame_length;
            dst_mac_stats[dst_mac] += frame_length;
            src_ip_stats[src_ip] += frame_length;
            dst_ip_stats[dst_ip] += frame_length;
        }

        log_file.close();

        // 输出统计信息
        std::cout << "来自不同MAC地址的通信数据长度统计：" << std::endl;
        for (const auto& pair : src_mac_stats) {
            std::cout << pair.first << ": " << pair.second << " bytes" << std::endl;
        }

        std::cout << "发至不同MAC地址的通信数据长度统计：" << std::endl;
        for (const auto& pair : dst_mac_stats) {
            std::cout << pair.first << ": " << pair.second << " bytes" << std::endl;
        }

        std::cout << "来自不同IP地址的通信数据长度统计：" << std::endl;
        for (const auto& pair : src_ip_stats) {
            std::cout << pair.first << ": " << pair.second << " bytes" << std::endl;
        }

        std::cout << "发至不同IP地址的通信数据长度统计：" << std::endl;
        for (const auto& pair : dst_ip_stats) {
            std::cout << pair.first << ": " << pair.second << " bytes" << std::endl;
        }
    }
}

int main() {
    char errbuf[PCAP_ERRBUF_SIZE];
    pcap_if_t* alldevs;
    pcap_if_t* d;
    int inum;
    int i = 0;

    // 获取所有可用的网络设备
    if (pcap_findalldevs(&alldevs, errbuf) == -1) {
        std::cerr << "Error in pcap_findalldevs: " << errbuf << std::endl;
        return 1;
    }

    // 打印所有可用的网络设备
    for (d = alldevs; d; d = d->next) {
        std::cout << ++i << ". " << (d->description ? d->description : d->name) << std::endl;
    }

    if (i == 0) {
        std::cerr << "未找到可用的网络设备！" << std::endl;
        pcap_freealldevs(alldevs);
        return 1;
    }

    std::cout << "请输入要使用的网络设备编号: ";
    std::cin >> inum;

    if (inum < 1 || inum > i) {
        std::cerr << "输入的编号无效！" << std::endl;
        pcap_freealldevs(alldevs);
        return 1;
    }

    // 找到用户选择的网络设备
    d = alldevs;
    for (i = 0; i < inum - 1; d = d->next, i++);

    pcap_t* handle;
    // 打开网络设备
    handle = pcap_open_live(d->name, BUFSIZ, 1, 1000, errbuf);
    if (handle == nullptr) {
        std::cerr << "无法打开网络设备：" << errbuf << std::endl;
        pcap_freealldevs(alldevs);
        return 1;
    }

    // 释放设备列表
    pcap_freealldevs(alldevs);

    // 启动统计线程
    std::thread stats_thread(statistics);

    // 开始捕获数据包
    pcap_loop(handle, 0, packet_handler, nullptr);

    // 关闭网络设备
    pcap_close(handle);

    // 等待统计线程结束
    stats_thread.join();

    return 0;
}
